package com.sec.android.app.myfiles.update;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Environment;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Base64;

import com.sec.android.app.myfiles.info.AppConstants;
import com.sec.android.app.myfiles.log.Log;
import com.sec.android.app.myfiles.util.PreferenceUtils;
import com.sec.android.app.myfiles.util.UiUtils;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;

public class UpdateChecker {
    private static final String TAG = "UpdateChecker";
    private static final String PD_TEST_PATH = Environment.getDataDirectory().getAbsolutePath() + "/local/tmp/test.test";
    private static volatile UpdateChecker mInstance = null;
    private Context mAppContext;

    private static final String UPDATE_CHECK_URL = "http://vas.samsungapps.com/stub/stubUpdateCheck.as";
    private static final String GET_CHINA_URL = "http://cn-ms.samsungapps.com/getCNVasURL.as"; //for china
    private static final String MCC_OF_CHINA = "460"; //china

    private static final long UPDATE_CHECK_THRESHOLD_TIME = 24 * 60 * 60 * 1000; // 24 hours
    private static final int CONNECTION_TIMEOUT = 3000;

    private static boolean mNeedMyFilesDownload, mNeedCloudGatewayDownload;
    private boolean mLoadingProgress = false;

    private UpdateChecker(Context context) {
        mAppContext = context.getApplicationContext();
    }

    public static UpdateChecker getInstance(Context context) {
        if (mInstance == null) {
            synchronized (UpdateChecker.class) {
                if (mInstance == null) {
                    mInstance = new UpdateChecker(context);
                }
            }
        }
        return mInstance;
    }

    // Stub code table
    public static class StubCodes {
        public static final String UPDATE_CHECK_FAIL = "-1";
        public static final String UPDATE_CHECK_NO_MATCHING_APPLICATION = "0";
        public static final String UPDATE_CHECK_UPDATE_NOT_NECESSARY = "1";
        public static final String UPDATE_CHECK_UPDATE_AVAILABLE = "2";
    }

    public interface StubUpdateCheckListener {
        void onUpdateCheckResult(boolean hasMyFilesUpdate, boolean hasCloudUpdate);
    }

    // Stub data bean
    public static class StubData {
        public String appId;
        public String resultCode;
        public String resultMsg;
        public String versionCode;
        public String versionName;
        public String contentSize;
    }

    // API request task
    private class StubRequest extends AsyncTask<String, Void, ArrayList<StubData>> {
        private String url;
        private StubUpdateCheckListener updateCheckListener;

        private StubRequest(String url) {
            this.url = url;
        }

        private void setUpdateCheckListener(StubUpdateCheckListener listener) {
            updateCheckListener = listener;
        }

        private void run() {
            executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url);
        }

        private ArrayList<StubData> parseUpdateCheckResult(StringBuilder response) {
            ArrayList<StubData> stubDataList = new ArrayList<>();
            try {
                XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
                XmlPullParser parser = factory.newPullParser();
                parser.setInput(new StringReader(response.toString()));
                //parser.setInput(connection.getInputStream(), "UTF-8");

                int eventType = parser.getEventType();
                String resultCode = "";
                boolean bCommonErrorEnd = false;
                while (!bCommonErrorEnd) {
                    switch (eventType) {
                        case XmlPullParser.START_TAG:
                            switch (parser.getName()) {
                                case "resultCode":
                                    resultCode = parser.nextText();
                                    break;
                                case "resultMsg":
                                    parser.nextText();
                                    bCommonErrorEnd = true;
                                    break;
                            }
                            break;
                    }
                    eventType = parser.next();
                }

                if (resultCode.isEmpty() || !(resultCode.equals("0"))) {
                    Log.e(this, "updateCheckRequest error");
                    return null;
                }

                StubData stubData = new StubData();
                while (eventType != XmlPullParser.END_DOCUMENT) {
                    switch (eventType) {
                        case XmlPullParser.START_TAG:
                            switch (parser.getName()) {
                                case "appId":
                                    stubData = new StubData();
                                    stubData.appId = parser.nextText();
                                    break;
                                case "resultCode":
                                    stubData.resultCode = parser.nextText();
                                    break;
                                case "resultMsg":
                                    stubData.resultMsg = parser.nextText();
                                    break;
                                case "versionName":
                                    stubData.versionName = parser.nextText();
                                    break;
                                case "versionCode":
                                    stubData.versionCode = parser.nextText();
                                    if ((stubData.versionCode != null) && !stubData.versionCode.isEmpty()) {
                                        PreferenceUtils.setLatestVersion(mAppContext, Integer.parseInt(stubData.versionCode), !AppConstants.CLOUD_GATEWAY_PACKAGE_NAME.equals(stubData.appId));
                                    }
                                    break;
                                case "contentSize":
                                    stubData.contentSize = parser.nextText();
                                    stubDataList.add(stubData);
                                    break;
                            }
                            break;
                    }
                    eventType = parser.next();
                }
                PreferenceUtils.setLastUpdateDate(mAppContext, System.currentTimeMillis());
            } catch (XmlPullParserException e) {
                e.printStackTrace();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            return stubDataList;
        }

        @Override
        protected ArrayList<StubData> doInBackground(String... params) {
            ArrayList<StubData> ret = null;
            try {
                String requestUrl = params[0];
                if (MCC_OF_CHINA.equals(getMcc())) {
                    String chinaURL = getChinaURL();
                    if (!chinaURL.isEmpty()) {
                        requestUrl = requestUrl.replaceFirst("vas.samsungapps.com", chinaURL);
                    }
                }
                URL url = new URL(requestUrl);
                Log.d(TAG, "requestUrl: " + requestUrl);

                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setConnectTimeout(CONNECTION_TIMEOUT);
                connection.setReadTimeout(CONNECTION_TIMEOUT);
                connection.setInstanceFollowRedirects(true);

                if (HttpURLConnection.HTTP_OK != connection.getResponseCode()) {
                    throw new IOException("status code " + connection.getResponseCode() + " != " + HttpURLConnection.HTTP_OK);
                }

                String line;
                StringBuilder response = new StringBuilder();
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
                    while ((line = reader.readLine()) != null) {
                        Log.d(TAG, "line:" + line);
                        response.append(line);
                    }
                }

                ret = parseUpdateCheckResult(response);
            } catch (IOException rex) {
                rex.printStackTrace();
            }
            return ret;
        }

        @Override
        protected void onPostExecute(ArrayList<StubData> dataList) {
            if (dataList == null || dataList.isEmpty()) {
                Log.e(this, "request URL is fail");
            }

            if (dataList != null && !dataList.isEmpty()) {
                for (StubData data : dataList) {
                    if (data.appId.equals(AppConstants.CLOUD_GATEWAY_PACKAGE_NAME)) {
                        mNeedCloudGatewayDownload = isUpdateAvailable(data);
                    } else {
                        mNeedMyFilesDownload = isUpdateAvailable(data);
                    }
                }
            }
            updateCheckListener.onUpdateCheckResult(mNeedMyFilesDownload, mNeedCloudGatewayDownload);
        }
    }

    // Need checking limit for application version via galaxyApps due to server overload.
    public boolean canServerCheck() {
        if (UiUtils.isNetworkOn(mAppContext)) {
            long lastCheckDate = PreferenceUtils.getLastUpdateDate(mAppContext, 0);
            long curDate = System.currentTimeMillis();

            Log.d(this, "canServerCheck() - getUpdateApkInfo " + (curDate - lastCheckDate) / 1000 / 60 / 60 + " hours ago"
                    + ". last: " + DateFormat.getInstance().format(new Date(lastCheckDate))
                    + ", current: " + DateFormat.getInstance().format(new Date(curDate)));

            if ((curDate - lastCheckDate) >= UPDATE_CHECK_THRESHOLD_TIME) {
                return true;
            }
        }
        return false;
    }

    private boolean checkUpdateFromPreference(boolean isMyFiles) {
        String checkingApp = isMyFiles ? "MyFiles" : "CloudGateway";
        String curVersion = getVersionCode(isMyFiles);
        int curVerCode = (curVersion != null && !curVersion.isEmpty()) ? Integer.parseInt(curVersion) : -1;
        int latestVerCode = PreferenceUtils.getLatestVersion(mAppContext, curVerCode, isMyFiles);

        Log.d(this, checkingApp + " getUpdateApkInfo lastVersion: " + latestVerCode + ", curVersion: " + curVersion);
        boolean bRet = (curVerCode != -1) && (latestVerCode > curVerCode);
        mNeedMyFilesDownload = (isMyFiles) ? bRet : mNeedMyFilesDownload;
        mNeedCloudGatewayDownload = (!isMyFiles) ? bRet : mNeedCloudGatewayDownload;

        return bRet;
    }

    public APKDownloadManager.Result needUpdate(boolean checkMyFilesOnly) {
        boolean needMyFilesUpdate = checkUpdateFromPreference(true);
        boolean needCloudUpdate = (!checkMyFilesOnly && checkUpdateFromPreference(false));
        return new APKDownloadManager.Result(needMyFilesUpdate, needCloudUpdate, needMyFilesUpdate || needCloudUpdate);
    }

    public void getUpdateApkInfo(StubUpdateCheckListener listener, boolean checkMyFilesOnly) {
        Uri.Builder builder = Uri.parse(UPDATE_CHECK_URL).buildUpon();
        if (checkMyFilesOnly) {
            builder.appendQueryParameter("appId", AppConstants.MY_FILES_PACKAGE_NAME + "@" + getVersionCode(true));
        } else {
            builder.appendQueryParameter("appId", AppConstants.MY_FILES_PACKAGE_NAME + "@" + getVersionCode(true) + ":" + AppConstants.CLOUD_GATEWAY_PACKAGE_NAME + "@" + getVersionCode(false));
        }
        builder.appendQueryParameter("deviceId", getDeviceId())
                .appendQueryParameter("mcc", getMcc())
                .appendQueryParameter("mnc", getMnc())
                .appendQueryParameter("csc", getCsc())
                .appendQueryParameter("sdkVer", getSdkVer())
                .appendQueryParameter("pd", getPd());

        Log.i(this, "getUpdateApkInfo : " + builder);

        StubRequest request = new StubRequest(builder.toString());
        request.setUpdateCheckListener(listener);
        request.run();
    }

    private String getVersionCode(boolean isMyFiles) {
        PackageInfo packageInfo;
        String ret = "";
        try {
            packageInfo = mAppContext.getPackageManager().getPackageInfo(isMyFiles ? AppConstants.MY_FILES_PACKAGE_NAME : AppConstants.CLOUD_GATEWAY_PACKAGE_NAME, 0);
            ret = String.valueOf(packageInfo.versionCode);
        } catch (NameNotFoundException ex) {
            Log.e(TAG, "getVersionCode: NameNotFoundException");
        }
        return ret;
    }

    private String getDeviceId() {
        return Build.MODEL.replaceFirst("SAMSUNG-", "");
    }

    private String getMcc() {
        TelephonyManager tm = (TelephonyManager) mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
        String operator = tm.getSimOperator();
        if (operator != null && operator.length() > 3) {
            return operator.substring(0, 3);
        }
        return "";
    }

    private String getMnc() {
        TelephonyManager tm = (TelephonyManager) mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
        String operator = tm.getSimOperator();
        if (operator != null && operator.length() > 3) {
            return operator.substring(3);
        }
        return "";
    }

    private static String getCsc() {
        String csc = "NONE";
        return csc;
    }

    private String getSdkVer() {
        return String.valueOf(VERSION.SDK_INT);
    }

    private String getEncImei() {
        TelephonyManager tm = (TelephonyManager) mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
        String uniqueId = tm.getDeviceId();
        if (TextUtils.isEmpty(uniqueId)) {
            uniqueId = Build.MODEL + Build.SERIAL;
        }
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(uniqueId.getBytes("iso-8859-1"), 0, uniqueId.length());
            byte[] hash = md.digest();
            return Base64.encodeToString(hash, Base64.DEFAULT);
        } catch (Exception ex) {
            Log.e(TAG, ex.getMessage());
            return "";
        }
    }

    public static boolean isPredeployMode() {
        File file = new File(PD_TEST_PATH);
        return file.exists();
    }

    private String getPd() {
        return isPredeployMode() ? "1" : "0";
    }

    private boolean isUpdateAvailable(StubData data) {
        return StubCodes.UPDATE_CHECK_UPDATE_AVAILABLE.equals(data.resultCode);
    }

    private String getChinaURL() {
        String cnVasUrl = PreferenceUtils.getChinaURL(mAppContext, "");
        long cnVasTime = PreferenceUtils.getChinaURLUpdateDate(mAppContext, 0L);

        if (!cnVasUrl.isEmpty() && ((System.currentTimeMillis() - cnVasTime) <= UPDATE_CHECK_THRESHOLD_TIME)) {
            Log.d(TAG, "getChinaURL : " + cnVasUrl);
            return cnVasUrl;
        }

        HttpURLConnection connection = null;

        try {
            URL url = new URL(GET_CHINA_URL);

            connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(CONNECTION_TIMEOUT);
            connection.setReadTimeout(CONNECTION_TIMEOUT);
            connection.setInstanceFollowRedirects(true);

            if (HttpURLConnection.HTTP_OK != connection.getResponseCode()) {
                throw new IOException("status code " + connection.getResponseCode() + " != " + HttpURLConnection.HTTP_OK);
            }

            String line;
            StringBuilder response = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
                while ((line = reader.readLine()) != null) {
                    Log.d(TAG, "line: " + line);
                    response.append(line);
                }
            }

            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser parser = factory.newPullParser();
            parser.setInput(new StringReader(response.toString()));
            //parser.setInput(connection.getInputStream(), "UTF-8");

            int eventType = parser.getEventType();
            while (eventType != XmlPullParser.END_DOCUMENT) {
                switch (eventType) {
                    case XmlPullParser.START_TAG:
                        String tagName = parser.getName();
                        if ("serverURL".equalsIgnoreCase(tagName)) {
                            cnVasUrl = parser.nextText();
                        }
                        break;
                }
                eventType = parser.next();
            }

            PreferenceUtils.setChinaURL(mAppContext, cnVasUrl);
            PreferenceUtils.setChinaURLUpdateDate(mAppContext, System.currentTimeMillis());
            Log.d(TAG, "getChinaURL: " + cnVasUrl);
        } catch (RuntimeException rex) {
            throw rex;
        } catch (Exception ex) {
            Log.d(TAG, ex.toString());
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
        return cnVasUrl;
    }

    public void setLoadingProgress(boolean isLoading) {
        mLoadingProgress = isLoading;
    }

    public boolean isLoading() {
        return mLoadingProgress;
    }

    public void callGalaxyAppsDeepLink(Context context, String packageName) {
        Intent intent = new Intent();
        String prefix = "samsungapps://ProductDetail/";
        intent.setData(Uri.parse(prefix + packageName));
        intent.putExtra("type", "cover");
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP
                | Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
        context.startActivity(intent);
    }
}